package com.park.gateway.filters;

import com.park.gateway.constants.RedisKeys;
import com.park.gateway.entity.IpWhiteList;
import com.park.gateway.mapper.IpWhiteListMapper;
import com.park.gateway.utils.IPUtils;
import com.park.gateway.utils.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.CollectionUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName WhiteIpListGatewayFilterFactory
 * @Description IP白名单校验过滤器
 * @Date 2023/6/9 14:45
 * @Author huxiaohua
 */
@Slf4j
public class IpWhiteListGatewayFilterFactory extends AbstractGatewayFilterFactory<IpWhiteListGatewayFilterFactory.Config> {
    private static final String IP_WHITE_LIST_KEY = "PAYCODE:API-GATEWAY:IP_WHITE_LIST";
    private IpWhiteListMapper ipWhiteListMapper;
    private RedisUtils redisUtils;
    private RedissonClient redissonClient;

    public IpWhiteListGatewayFilterFactory(IpWhiteListMapper ipWhiteListMapper, RedisUtils redisUtils, RedissonClient redissonClient) {
        super(Config.class);
        this.ipWhiteListMapper = ipWhiteListMapper;
        this.redisUtils = redisUtils;
        this.redissonClient = redissonClient;
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("flag", "whiteIps");
    }

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            if (!config.getFlag()) {
                return chain.filter(exchange);
            }
            ServerHttpRequest request = exchange.getRequest();
            String remoteIp = IPUtils.getIpAddr(request);
            log.info("remoteIp::::::::{}",remoteIp);
            String ips = config.getWhiteIps();
            if (ips != null && ips.contains(remoteIp)) {
                return chain.filter(exchange);
            }
            if (redisUtils.scontains(IP_WHITE_LIST_KEY, remoteIp)) {
                return chain.filter(exchange);
            }
            if (!redisUtils.contains(IP_WHITE_LIST_KEY)) {
                RLock lock = redissonClient.getLock(RedisKeys.TRANS_CONCURRENCY_KEY_PREFIX + "IpWhiteList");
                try {
                    if (!lock.tryLock(10, 10, TimeUnit.SECONDS)) {
                        throw new RuntimeException("系统繁忙，请稍后再试");
                    }
                    if (!redisUtils.contains(IP_WHITE_LIST_KEY)) {
                        List<IpWhiteList> ipWhiteLists = ipWhiteListMapper.selectList(null);
                        if (!CollectionUtils.isEmpty(ipWhiteLists)) {
                            String[] ipArr = new String[ipWhiteLists.size()];
                            for (int i = 0; i < ipWhiteLists.size(); i++) {
                                ipArr[i] = ipWhiteLists.get(i).getIp();
                            }
                            redisUtils.sadd(IP_WHITE_LIST_KEY, 2 * 60 * 60, ipArr);
                        }
                    }
                } catch (InterruptedException e) {
                    log.error("获取锁异常", e);
                    throw new RuntimeException("系统繁忙，请稍后再试");
                } finally {
                    if (lock.isHeldByCurrentThread()) {
                        lock.unlock();
                    }
                }
            }
            if (redisUtils.scontains(IP_WHITE_LIST_KEY, remoteIp)) {
                return chain.filter(exchange);
            }
            log.info("异常ip ====> {}", remoteIp);
            throw new RuntimeException("暂无访问权限");
        };
    }

    static class Config{
        /**
         * IP校验开关
         */
        private Boolean flag;
        /**
         * ip白名单
         */
        private String whiteIps;

        public Boolean getFlag() {
            return flag;
        }

        public void setFlag(Boolean flag) {
            this.flag = flag;
        }

        public String getWhiteIps() {
            return whiteIps;
        }

        public void setWhiteIps(String whiteIps) {
            this.whiteIps = whiteIps;
        }
    }
}
